﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Collections.Specialized;
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BrainTechLLC.ThreadSafeObjects;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace BrainTechLLC
{
    [Serializable]
    [DataContract]
    public partial class PropertyData : IDictionary<string, IdentifierAndValue>
    {
        [DataMember]
        public ThreadSafeLookup<string, IdentifierAndValue> _props;

        [XmlIgnore]
        public List<IdentifierAndValue> DisplayableProps
        {
            get
            {
                List<IdentifierAndValue> results = new List<IdentifierAndValue>();
                List<IdentifierAndValue> all = Props.AllItems;
                foreach (var item in all)
                {
                    if (item._identifier.StartsWith("Max", StringComparison.OrdinalIgnoreCase))
                        continue;

                    string max = "Max" + item._identifier;
                    IdentifierAndValue val2 = _props[max];
                    if (val2 != null)
                    {
                        string s = item._value.ToString() + " / " + val2._value.ToString();
                        results.Add(new IdentifierAndValue(item._identifier, s));
                    }
                    else
                    {
                        results.Add(item);
                    }
                }
                return results;
            }
        }

        [XmlIgnore]
        public ThreadSafeLookup<string, IdentifierAndValue> Props
        {
            get
            {
                if (_props == null)
                    Interlocked.CompareExchange<ThreadSafeLookup<string, IdentifierAndValue>>(ref _props, new ThreadSafeLookup<string, IdentifierAndValue>(), null);

                return _props;
            }
            set
            {
                if (!_props.AreEqual(value))
                {
                    OnPropertyChanging("Props", _props, value);
                    _props = value;
                    OnPropertyChanged("Props", _props, value);
                }
            }
        }

        public PropertyData() { }

        public PropertyData(params IdentifierAndValue[] values)
        {
            AddProperties(values);
        }

        public void AddProperties(params IdentifierAndValue[] values)
        {
            foreach (var v in values)
                Props.Add(v.Identifier, v);
        }

        public void AddPairedProperty(string propertyName, string prefix, int value, int max)
        {
            Props.AddOrSet(propertyName, new IdentifierAndValue() { Identifier = propertyName, Value = value });
            Props.AddOrSet(prefix + propertyName, new IdentifierAndValue() { Identifier = prefix + propertyName, Value = max });
        }

        public void AddProperty(string propertyName, object value)
        {
            Props.AddOrSet(propertyName, new IdentifierAndValue() { Identifier = propertyName, Value = value });
        }

        public IdentifierAndValue GetReference(string identifier)
        {
            if (_props == null || !_props.ContainsKey(identifier))
                return null;

            return _props[identifier];
        }

        public PropertyData MakeCopy()
        {
            if (_props == null)
                return null;

            PropertyData p = new PropertyData();
            var v = _props.AllKeyValuePairs;
            p._props = new ThreadSafeLookup<string, IdentifierAndValue>();

            foreach (var kvp in v)
            {
                KeyValuePair<string, IdentifierAndValue> newKvp = new KeyValuePair<string, IdentifierAndValue>(kvp.Key, new IdentifierAndValue() { Identifier = kvp.Key, Value = kvp.Value.Value });
                p._props.Add(newKvp);
            }
            return p;
        }

        public T Read<T>(string s)
        {
            IdentifierAndValue o = Props[s];
            return (o == null) ? default(T) : (T)o.Value;
        }

        public override string ToString()
        {
            List<IdentifierAndValue> properties = DisplayableProps;
            properties.Sort((o1, o2) => o1._identifier.CompareTo(o2._identifier));
            return properties.BuildList(",", (kvp) => kvp._identifier + "=" + kvp._value.ToString());
        }

        public T GetArgument<T>(string identifier)
        {
            IdentifierAndValue v = GetArgumentReference(identifier);
            return v == null ? default(T) : (T)v.Value;
        }

        public IdentifierAndValue GetArgumentReference(string identifier)
        {
            if (_props == null)
                return null;

            return _props[identifier];
        }

        public T GetArgument<T>(string identifier, object defaultValue)
        {
            IdentifierAndValue v = GetArgumentReference(identifier);
            return v == null ? (T)Convert.ChangeType(defaultValue, typeof(T), null) : (T)v.Value;
        }

        #region IDictionary<string,IdentifierAndValue> Members

        public void Add(string key, IdentifierAndValue value)
        {
            Props.AddOrSet(key, value);
        }

        public bool ContainsKey(string key)
        {
            return _props == null ? false : Props.ContainsKey(key);
        }

        public ICollection<string> Keys
        {
            get { return Props.Keys; }
        }

        public bool Remove(string key)
        {
            return Props.Remove(key);
        }

        public bool TryGetValue(string key, out IdentifierAndValue value)
        {
            return Props.TryGetValue(key, out value);
        }

        public ICollection<IdentifierAndValue> Values
        {
            get { return Props.Values; }
        }

        public IdentifierAndValue this[string key]
        {
            get
            {
                return Props[key];
            }
            set
            {
                Props.AddOrSet(key, value);
            }
        }

        #endregion

        #region ICollection<KeyValuePair<string,IdentifierAndValue>> Members

        public void Add(KeyValuePair<string, IdentifierAndValue> item)
        {
            Props.AddOrSet(item.Key, item.Value);
        }

        public void Clear()
        {
            Props.Clear();
        }

        public bool Contains(KeyValuePair<string, IdentifierAndValue> item)
        {
            return Props.Contains(item);
        }

        public void CopyTo(KeyValuePair<string, IdentifierAndValue>[] array, int arrayIndex)
        {
            Props.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return (_props == null) ? 0 : Props.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(KeyValuePair<string, IdentifierAndValue> item)
        {
            return Props.Remove(item);
        }

        #endregion

        #region IEnumerable<KeyValuePair<string,IdentifierAndValue>> Members

        public IEnumerator<KeyValuePair<string, IdentifierAndValue>> GetEnumerator()
        {
            return Props.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    public partial class PropertyData : INotifyPropertyChanged, INotifyCollectionChanged, INotifyPropertyChanging
    {
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

#if NO_SILVERLIGHT
        [field: NonSerialized]
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        [field: NonSerialized]
        public event PropertyChangingEventHandler PropertyChanging;
#else
        [field: NonSerialized]
        public event EventHandler<NotifyCollectionChangedEventArgsEx<IdentifierAndValue>> CollectionChanged;

        [field: NonSerialized]
        public event EventHandler<PropertyChangingEventArgsEx> PropertyChanging;
#endif

        public virtual void RegisterForPropertyChange()
        {
#if NO_SILVERLIGHT
            Props.CollectionChanged += new NotifyCollectionChangedEventHandler(Properties_CollectionChanged);
#else
            Props.CollectionChanged += new EventHandler<NotifyCollectionChangedEventArgsEx<IdentifierAndValue>>(Props_CollectionChanged);
#endif
        }

        void Props_CollectionChanged(object sender, NotifyCollectionChangedEventArgsEx<IdentifierAndValue> e)
        {
            if (CollectionChanged != null)
                CollectionChanged(this.Props, e);
        }

#if NO_SILVERLIGHT
        void Properties_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
                CollectionChanged(this.Props, e);
        }
#endif

        public virtual void OnPropertyChanged(string property, object oldValue, object newValue)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgsEx(property) { OldValue = oldValue, NewValue = newValue });
        }

        public virtual void OnPropertyChanging(string property, object oldValue, object newValue)
        {
            if (PropertyChanging != null)
                PropertyChanging(this, new PropertyChangingEventArgsEx(property) { OldValue = oldValue, NewValue = newValue });
        }
    }

    public static class ConvertToObservableCollection
    {
        public static ObservableCollection<T> ToObservable<T>(this IEnumerable<T> items)
        {
            ObservableCollection<T> props = new ObservableCollection<T>();
            foreach (var p in items)
            {
                props.Add(p);
            }
            return props;
        }
    }
}
